/*
 Copyright (C) 2011 QDSS.org. All rights reserved.
 QDSS.org PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package org.qdss.oapi4j.apis.paipai;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.TreeMap;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.dom4j.Element;
import org.qdss.commons.util.EncryptUtils;
import org.qdss.oapi4j.ApiInvoker;
import org.qdss.oapi4j.ApiParameter;
import org.qdss.oapi4j.InvokeFailureException;
import org.qdss.oapi4j.Result;
import org.qdss.oapi4j.base.BadResultException;
import org.qdss.oapi4j.base.BaseResult;
import org.qdss.oapi4j.base.JsonResult;
import org.qdss.oapi4j.base.XmlResult;
import org.qdss.oapi4j.util.ApiUtils;

/**
 * 拍拍Api 调用者
 * 
 * @author <a href="mailto:zhaofang123@gmail.com">Zhao Fangfang</a>
 * @date 2011-4-6
 * @version $Id: PaipaiApiInvoker.java 17 2011-04-06 10:40:49Z
 *          zhaofang123@gmail.com $
 */
public class PaipaiApiInvoker<T> extends ApiInvoker<T> {

	/**
	 * @param spid
	 * @param seckey
	 * @param clazz
	 */
	public PaipaiApiInvoker(String spid, String seckey, Class<?> clazz) {
		super(spid, seckey, clazz);
	}
	
	@Override
	public ApiParameter createApiParameter(String apiUrl, String user, String token) {
		ApiParameter parameter = new ApiParameter(apiUrl, token);
		parameter.addParam("spid", appKey);
		parameter.addParam("seckey", appSecret);
		parameter.addParam("uin", user);
		parameter.addParam("token", token);
		if (getResultGenricType() == Element.class) {
			parameter.addParam("format", "xml");
		} else {
			parameter.addParam("format", "json");
		}
		return parameter;
	}

	@Override
	protected Result<T> invokeMethod(ApiParameter param, boolean isGet)
			throws InvokeFailureException {
		Result<T> result = null;
		try {
			result = invokeMethod0(param, isGet, 1);
		} catch (Exception ex) {
			throw new InvokeFailureException(ex);
		}
		return result;
	}
	
	/**
	 * @param param
	 * @param isGet
	 * @param retry
	 * @return
	 * @throws IOException
	 */
	private Result<T> invokeMethod0(ApiParameter param, boolean isGet, int retry)
			throws IOException {
		final Map<String, String> signParams = new TreeMap<String, String>();
		
		StringBuffer url = new StringBuffer(param.getApiUrl()).append('?')
				.append("charset=").append(ApiUtils.ENCODING_DEFAULT);
		signParams.put("charset", ApiUtils.ENCODING_DEFAULT);

		HttpPost httpPost = new HttpPost(url.toString());

		final Charset charset = Charset.forName(ApiUtils.ENCODING_DEFAULT);
		MultipartEntity entity = new MultipartEntity(HttpMultipartMode.STRICT,
				null, charset);
		for (Map.Entry<String, Object> e : param.getParams().entrySet()) {
			String key = e.getKey();
			Object val = e.getValue();
			if (val instanceof String || val instanceof Number) {
				entity.addPart(key, new StringBody(val.toString(), charset));
				signParams.put(key, val.toString());
			} else if (val instanceof File) {
				entity.addPart(key, new FileBody((File) val));
			}
		}

		String sign = makeSign(httpPost.getURI().getPath(), signParams);
		entity.addPart("sign", new StringBody(sign, charset));
		httpPost.setEntity(entity);
		
		long start = System.currentTimeMillis();
		HttpResponse response = getHttpClientFetcher().execute(httpPost);
		
		String resultText;
		try {
			resultText = getHttpClientFetcher().parseResponseText(response);
		} catch (IOException ex) {
			throw new BadResultException("Could't parse result from response!", ex);
		}

		int time = (int) (System.currentTimeMillis() - start);
		Header headers[] = response.getAllHeaders();
		@SuppressWarnings("unchecked")
		BaseResult<T> result = (getResultGenricType() == Element.class)
				? (BaseResult<T>) new XmlResult(resultText, time, headers)
				: (BaseResult<T>) new JsonResult(resultText, time, headers);
		return new PaipaiResult<T>(result);
	}

	/**
	 * 签名
	 * 
	 * @param aUri
	 * @param signParams
	 * @return
	 * @throws IOException
	 */
	private String makeSign(String aUri, Map<String, String> signParams) throws IOException {
		if (aUri.startsWith("/")) {
			aUri = aUri.substring(1);
		}
		if (aUri.endsWith(".xhtml")) {
			aUri = aUri.substring(0, aUri.length() - 6);
		}
		aUri = aUri.replaceAll("/", ".");

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		final String enc = ApiUtils.ENCODING_DEFAULT;
		out.write(aUri.getBytes(enc));
		for (Map.Entry<String, String> e : signParams.entrySet()) {
			out.write(e.getKey().getBytes(enc));
			out.write(e.getValue().getBytes(enc));
		}
		out.write(appSecret.getBytes(enc));
		byte[] array = out.toByteArray();
		return EncryptUtils.toMD5Hex(array);
	}
}
